using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;

using Lincolnwood.Foundation.Scheduling;

namespace Lincolnwood.Foundation.Scheduling.Tests
{
    [TestClass]
    public class DailyRecurrencePatternTests
    {
        private MockRepository mockMaker;

        [TestInitialize]
        public void Init()
        {
            mockMaker = new MockRepository();
        }

        [TestMethod]
        public void Expand_WithEventDurationShorterThanRecurrenceInterval_AndAlignedExpandWindow_Expands()
        {
            // establish values for a IEventDefinition stand-in
            string testName = "Test Name";
            string testDesc = "Test Recurring Event";       
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");  // when the event starts
            DateTime testEnd = DateTime.Parse("1/1/2000 2:00 pm");  // when the event ends
            DateTime expandWindowBegin = DateTime.Parse("1/1/2000"); // we will start expanding recurrences from
            DateTime expandWindowEnd = DateTime.Parse("1/10/2000"); // we will stop expanding recurrences on
           
            // the event will recur daily until 1/9
            DateTime testRecurrenceEnd = DateTime.Parse("1/9/2000");

            // set up the mock object using the mocking framework
            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // set up the recurrent pattern that can be plugged in and given to the event expander
            DailyRecurrencePattern testPattern = new DailyRecurrencePattern(testRecurrenceEnd, 1);

            // expand the event occurances out
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(9, expandedEvents.Count);

            for (int i = 1; i <= 9; i++)
            {
                int begin = i;
                int end = i;
                int index = i - 1;
                Assert.AreEqual(DateTime.Parse(String.Format("1/{0}/2000 1:00 pm", begin)), expandedEvents[index].StartTime);
                Assert.AreEqual(DateTime.Parse(String.Format("1/{0}/2000 2:00 pm", end)), expandedEvents[index].EndTime);
            }
        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndAlignedExpandWindow_Expands()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("1/3/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("1/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            DailyRecurrencePattern testPattern = new DailyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("1/1/2000");
            DateTime expandWindowEnd = DateTime.Parse("1/10/2000");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(9, expandedEvents.Count);

            for (int i = 1; i <= 9; i++)
            {
                int begin = i;
                int end = i + 2;
                int index = i - 1;
                Assert.AreEqual(DateTime.Parse(String.Format("1/{0}/2000 1:00 pm", begin)), expandedEvents[index].StartTime);
                Assert.AreEqual(DateTime.Parse(String.Format("1/{0}/2000 2:00 pm", end)), expandedEvents[index].EndTime);
            }
        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndLargerExpandWindow_Expands()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("1/1/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("1/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            DailyRecurrencePattern testPattern = new DailyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("11/1/1999");
            DateTime expandWindowEnd = DateTime.Parse("1/10/2000");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(9, expandedEvents.Count);

            for (int i = 1; i <= 9; i++)
            {
                int begin = i;
                int end = i;
                int index = i - 1;
                Assert.AreEqual(DateTime.Parse(String.Format("1/{0}/2000 1:00 pm", begin)), expandedEvents[index].StartTime);
                Assert.AreEqual(DateTime.Parse(String.Format("1/{0}/2000 2:00 pm", end)), expandedEvents[index].EndTime);
            }
        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndExpandWindowThatStartsAfterTheEventStart_Expands()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("1/1/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("1/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            DailyRecurrencePattern testPattern = new DailyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("1/5/2000");
            DateTime expandWindowEnd = DateTime.Parse("1/10/2000");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(5, expandedEvents.Count);

            for (int i = 5; i <= 9; i++)
            {
                int begin = i;
                int end = i;
                int index = i - 5;
                Assert.AreEqual(DateTime.Parse(String.Format("1/{0}/2000 1:00 pm", begin)), expandedEvents[index].StartTime);
                Assert.AreEqual(DateTime.Parse(String.Format("1/{0}/2000 2:00 pm", end)), expandedEvents[index].EndTime);
            }
        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndExpandWindowThatStartsAndEndsAfterTheEventRecurrenceEnd_DoesNotExpand()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("1/1/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("1/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            DailyRecurrencePattern testPattern = new DailyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("1/10/2000");
            DateTime expandWindowEnd = DateTime.Parse("1/20/2000");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(0, expandedEvents.Count);
        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndExpandWindowThatStartsAndEndsBeforeTheEventBegin_DoesNotExpand()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("1/1/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("1/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            DailyRecurrencePattern testPattern = new DailyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("12/30/1999");
            DateTime expandWindowEnd = DateTime.Parse("12/31/1999");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(0, expandedEvents.Count);
        }

        [TestMethod]
        public void Expand_WithRecurrencePatternException_OmitsAppropriateEventOccurance()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("1/1/2000 5:00 pm");
            string testDesc = "Event used for unit testing daily recurring events during a window beginning during " +
                "the period of event occurance and ending during the period of event occurance";
            string testName = "Test Name";
            DateTime testRecurrenceEnd = DateTime.Parse("1/10/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();

            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            DailyRecurrencePattern testPattern = new DailyRecurrencePattern(testRecurrenceEnd, 1);


            // create an exception that omits one of the occurances
            testPattern.RemoveInstance(DateTime.Parse("1/5/2000"));

            // expand the occurances
            IList<IEventInstance> eventList = testPattern.Expand(mockEvent, DateTime.Parse("1/3/2000"), DateTime.Parse("1/7/2000"));

            // make sure that the recurrence got computed correctly
            Assert.AreEqual(4, eventList.Count);
        }

        [TestMethod]
        public void Expand_AfterAlteringOccurance_IncludesAppropriateAlterations()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("1/1/2000 5:00 pm");
            string testDesc = "Event used for unit testing daily recurring events during a window beginning during " +
                "the period of event occurance and ending during the period of event occurance";
            string testName = "Test Recurring Event";
            DateTime testRecurrenceEnd = DateTime.Parse("1/10/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();

            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // set daily recurrence
            DailyRecurrencePattern testPattern = new DailyRecurrencePattern(testRecurrenceEnd, 1);


            // create an exception that alters one of the occurances
            var eventException = testPattern.AlterInstance(DateTime.Parse("1/5/2000"),
                                "modified event", "modified event!",
                                DateTime.Parse("1/15/2000 6:00:00 pm"),
                                DateTime.Parse("1/15/2000 7:00:00 pm"));

            // expand the occurances
            IList<IEventInstance> eventList = testPattern.Expand(mockEvent, DateTime.Parse("1/3/2000"), DateTime.Parse("1/20/2000"));

            // make sure that the recurrence got computed correctly
            Assert.AreEqual(8, eventList.Count);

            // make sure that our event exception got included
            var actualEventExceptions = (from i in eventList
                                         where i.StartTime == DateTime.Parse("1/15/2000 6:00:00 pm")
                                             && i.EndTime == DateTime.Parse("1/15/2000 7:00:00 pm")
                                         select i).Count<IEventInstance>();

            Assert.AreEqual(1, actualEventExceptions);
        }
    }
}